/*
 * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package java.io;


/**
 * 用于写入字符流的抽象类。
 * 子类必须实现的唯一方法是write(char[]， int, int)， flush()和close()。
 * 然而，大多数子类会覆盖这里定义的一些方法，以提供更高的效率，额外的功能，或两者兼有。
 *
 * @see Writer
 * @see   BufferedWriter
 * @see   CharArrayWriter
 * @see   FilterWriter
 * @see   OutputStreamWriter
 * @see     FileWriter
 * @see   PipedWriter
 * @see   PrintWriter
 * @see   StringWriter
 * @see Reader
 *
 * @author      Mark Reinhold
 * @since       JDK1.1
 */

public abstract class Writer implements Appendable, Closeable, Flushable {

    /**
     * 用来保存对字符串和单个字符的写操作的临时缓冲区
     */
    private char[] writeBuffer;

    /**
     * writeBuffer的大小，必须是>= 1
     */
    private static final int WRITE_BUFFER_SIZE = 1024;

    /**
     * 用于同步此流上的操作的对象。
     * 更重要的是，字符流对象可以使用自身以外的对象来保护临界区。
     * 因此子类应该在这个字段中使用对象，而不是this或同步方法。
     */
    protected Object lock;

    /**
     * 创建一个新的字符流写入器，其临界区将在写入器本身上同步。
     */
    protected Writer() {
        this.lock = this;
    }

    /**
     * 创建一个新的字符流写入器，它的临界区将在给定对象上同步。
     *
     * @param  lock
     *         Object to synchronize on
     */
    protected Writer(Object lock) {
        if (lock == null) {
            throw new NullPointerException();
        }
        this.lock = lock;
    }

    /**
     * 只写一个字符。
     * 待写入的字符包含在给定整数值的16位低阶比特中;
     * 16个高阶位被忽略。
     *
     * <p> 打算支持高效单字符输出的子类应该重写此方法。
     *
     * @param  c
     *         int specifying a character to be written
     *
     * @throws  IOException
     *          If an I/O error occurs
     */
    public void write(int c) throws IOException {
        synchronized (lock) {
            if (writeBuffer == null){
                writeBuffer = new char[WRITE_BUFFER_SIZE];
            }
            // 先写入writeBuffer，再把writeBuffer写入输出流
            writeBuffer[0] = (char) c;
            write(writeBuffer, 0, 1);
        }
    }

    /**
     * 写入一个字符数组。
     *
     * @param  cbuf
     *         Array of characters to be written
     *
     * @throws  IOException
     *          If an I/O error occurs
     */
    public void write(char cbuf[]) throws IOException {
        write(cbuf, 0, cbuf.length);
    }

    /**
     * 写入一个字符数组的一部分。
     *
     * @param  cbuf
     *         Array of characters
     *
     * @param  off
     *         Offset from which to start writing characters
     *
     * @param  len
     *         Number of characters to write
     *
     * @throws  IOException
     *          If an I/O error occurs
     */
    abstract public void write(char cbuf[], int off, int len) throws IOException;

    /**
     * 写入一个字符串。
     *
     * @param  str
     *         String to be written
     *
     * @throws  IOException
     *          If an I/O error occurs
     */
    public void write(String str) throws IOException {
        write(str, 0, str.length());
    }

    /**
     * 写入一个字符串的一部分。
     *
     * @param  str
     *         A String
     *
     * @param  off
     *         Offset from which to start writing characters
     *
     * @param  len
     *         Number of characters to write
     *
     * @throws  IndexOutOfBoundsException
     *          If <tt>off</tt> is negative, or <tt>len</tt> is negative,
     *          or <tt>off+len</tt> is negative or greater than the length
     *          of the given string
     *
     * @throws  IOException
     *          If an I/O error occurs
     */
    public void write(String str, int off, int len) throws IOException {
        synchronized (lock) {
            char cbuf[];
            // 如果len小于等于WRITE_BUFFER_SIZE，cbuf为writeBuffer
            // 如果大于，建立一个len长度的数组，赋值给cbuf（注意：没有赋值给writeBuffer！)
            if (len <= WRITE_BUFFER_SIZE) {
                if (writeBuffer == null) {
                    writeBuffer = new char[WRITE_BUFFER_SIZE];
                }
                cbuf = writeBuffer;
            } else {    // 不要永久分配非常大的缓存
                cbuf = new char[len];
            }
            // str的元素复制到cbuf，然后cbuf写入输出流
            str.getChars(off, (off + len), cbuf, 0);
            write(cbuf, 0, len);
        }
    }

    /**
     * 向写入器追加指定的字符序列。
     *
     * <p> 对out.append(csq)表单的这个方法的调用行为与调用的方式完全相同
     *
     * <pre>
     *     out.write(csq.toString()) </pre>
     *
     * <p> 根据字符序列csq的toString规范，整个序列可能不会被附加。
     * 例如，调用字符缓冲区的toString方法将返回一个子序列，其内容取决于缓冲区的位置和限制。
     *
     * @param  csq
     *         The character sequence to append.  If <tt>csq</tt> is
     *         <tt>null</tt>, then the four characters <tt>"null"</tt> are
     *         appended to this writer.
     *
     * @return  This writer
     *
     * @throws  IOException
     *          If an I/O error occurs
     *
     * @since  1.5
     */
    public Writer append(CharSequence csq) throws IOException {
        if (csq == null)
            write("null");
        else
            write(csq.toString());
        return this;
    }

    /**
     * 向写入器追加指定字符序列的子序列。
     *
     * <p> 当csq不是null时，调用此方法的形式out.append(csq, start,end)与调用的方式完全相同
     *
     * <pre>
     *     out.write(csq.subSequence(start, end).toString()) </pre>
     *
     * @param  csq
     *         The character sequence from which a subsequence will be
     *         appended.  If <tt>csq</tt> is <tt>null</tt>, then characters
     *         will be appended as if <tt>csq</tt> contained the four
     *         characters <tt>"null"</tt>.
     *
     * @param  start
     *         The index of the first character in the subsequence
     *
     * @param  end
     *         The index of the character following the last character in the
     *         subsequence
     *
     * @return  This writer
     *
     * @throws  IndexOutOfBoundsException
     *          If <tt>start</tt> or <tt>end</tt> are negative, <tt>start</tt>
     *          is greater than <tt>end</tt>, or <tt>end</tt> is greater than
     *          <tt>csq.length()</tt>
     *
     * @throws  IOException
     *          If an I/O error occurs
     *
     * @since  1.5
     */
    public Writer append(CharSequence csq, int start, int end) throws IOException {
        CharSequence cs = (csq == null ? "null" : csq);
        write(cs.subSequence(start, end).toString());
        return this;
    }

    /**
     * 向写入器追加单个字符的子序列。
     *
     * <p> 当csq不是null时，调用此方法的形式out.append(c)与调用的方式完全相同
     *
     * <pre>
     *     out.write(c) </pre>
     *
     * @param  c
     *         The 16-bit character to append
     *
     * @return  This writer
     *
     * @throws  IOException
     *          If an I/O error occurs
     *
     * @since 1.5
     */
    public Writer append(char c) throws IOException {
        write(c);
        return this;
    }

    /**
     * 刷新流。
     * 如果流在缓冲区中保存了各种write()方法中的任何字符，请立即将它们写入预期的目的地。
     * 然后，如果目的地是另一个字符或字节流，刷新它。
     * 因此，一次flush()调用将刷新写入器和输出流链中的所有缓冲区。
     *
     * <p> 如果这个流的目的是底层操作系统提供的抽象，例如文件，
     * 那么刷新流只能保证先前写入到流的字节被传递给操作系统进行写入;
     * 它不能保证它们确实被写入物理设备，如磁盘驱动器。
     *
     * @throws  IOException
     *          If an I/O error occurs
     */
    abstract public void flush() throws IOException;

    /**
     * 关闭流，首先flush。
     * 一旦流被关闭，进一步的write()或flush()调用将导致抛出IOException。
     * 关闭以前关闭的流没有任何效果。
     *
     * @throws  IOException
     *          If an I/O error occurs
     */
    abstract public void close() throws IOException;

}
